<?php
/////////////////////////////////////////////////////////////////
// SpeedPHP中文PHP框架, Copyright (C) 2008 - 2010 SpeedPHP.com //
/////////////////////////////////////////////////////////////////

/**
 * spController 基础控制器程序父类 应用程序中的每个控制器程序都应继承于spController
 */
class spController { 

	/**
	 * 视图对象
	 */
	public $v;
	
	/**
	 * 赋值到模板的变量
	 */
	private $__template_vals = array();
	
	/**
	 * 构造函数
	 */
	public function __construct()
	{	
		$cache = new zxcache($GLOBALS['__controller'], $GLOBALS['__action']);
		$cache->run();
		if(TRUE == $GLOBALS['G_SP']['view']['enabled']){
			$this->v = spClass('spView');
		}
	}

    /**
     *
     * 跳转程序
     *
     * 应用程序的控制器类可以覆盖该函数以使用自定义的跳转程序
     *
     * @param $url  需要前往的地址
     * @param $delay   延迟时间
     */
    public function jump($url, $delay = 0){
		echo "<html><head><meta http-equiv='refresh' content='{$delay};url={$url}'></head><body></body></html>";
		exit;
    }

    /**
     *
     * 错误提示程序
     *
     * 应用程序的控制器类可以覆盖该函数以使用自定义的错误提示
     *
     * @param $msg   错误提示需要的相关信息
     * @param $url   跳转地址
     */
    public function error($msg, $url){
		echo "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"><script>function sptips(){alert(\"{$msg}\");location.href=\"{$url}\";}</script></head><body onload=\"sptips()\"></body></html>";
		exit;
    }

    /**
     *
     * 成功提示程序
     *
     * 应用程序的控制器类可以覆盖该函数以使用自定义的成功提示
	 *
     * @param $msg   成功提示需要的相关信息
     * @param $url   跳转地址
     */
    public function success($msg, $url){
		echo "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"><script>function sptips(){alert(\"{$msg}\");location.href=\"{$url}\";}</script></head><body onload=\"sptips()\"></body></html>";
		exit;
    }
    
    /**
	 * 页面跳转
	 *
	 * @param $url      跳转地址，可以直接写 URL或数组array('c'=>'controller', 'a'=>'action') 
	 * @param $message  跳转前显示信息
	 * @param $delay    延时跳转时间
	 */
	public static function redirect($url, $message = null, $delay = null, $top = 0) {
		if(is_array($url)) {
			if($url['c'] && $url['a']) {
				$url = (defined(WEB_ROOT)?WEB_ROOT:'') . 'index.php?c=' . $url['c'] . '&a=' . $url['a'];
			}
			else {
				return;
			}
		}
		else {
			$url = $url;
		}	

		if(null != $delay && intval($delay)>0) {
			if ($GLOBALS['G_SP']['view']['charset']){
				echo "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"\" />";
			}
			echo "<meta http-equiv=\"refresh\" content=\"{$delay};URL={$url}\" />";
			if(null !== $message) {
				echo $message;
			}
		}
		else if (null !== $message){
			if ($GLOBALS['G_SP']['view']['charset']){
				echo "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"\" />";
			}
			echo "<script type=text/javascript>";
			echo "alert(\"" . $message . "\");";
			echo ($top ? "top" : "window") . ".location.href=\"" . $url . "\";";
			echo "</script>";
		}
		else {
			header("Location: {$url}");
		}
		exit();
	}

	/**
	 * 魔术函数，获取赋值作为模板内变量
	 */
	public function __set($name, $value)
	{
		if(TRUE == $GLOBALS['G_SP']['view']['enabled'] && false !== $value){
			$this->v->engine->assign(array($name=>$value));
		}
		$this->__template_vals[$name] = $value;
	}
	

	/**
	 * 魔术函数，返回已赋值的变量值
	 */
	public function __get($name)
	{
		return $this->__template_vals[$name];
	}
	
	/**
	 * 输出模板
	 *
     * @param $tplname   模板路径及名称
     * @param $output   是否直接显示模板，设置成FALSE将返回HTML而不输出
	 */
	public function display($tplname, $output = TRUE)
	{
		if(TRUE == $GLOBALS['G_SP']['view']['enabled']){;
			$this->v->display($tplname);
		}else{
			extract($this->__template_vals);
			require($tplname);
		}
		if( TRUE != $output )return ob_get_clean();
	}
	
	/**
	 * 获得模板内容
	 *
	 * @param $tplfile   模板文件名
	 */
	public function fetch($tplname = null){
		return $this->v->fetch($tplname);
	}
	
	/**
	 * 自动输出页面
	 * @param tplname 模板文件路径
	 */
	public function auto_display($tplname)
	{
		if( TRUE != $this->v->displayed && FALSE != $GLOBALS['G_SP']['view']['auto_display']){
			if( !method_exists($this->v->engine, 'template_exists') || TRUE == $this->v->engine->template_exists($tplname) )
				$this->display($tplname);
		}
	}

	/**
	 * 魔术函数，实现对控制器扩展类的自动加载
	 */
	public function __call($name, $args)
	{
		if(in_array($name, $GLOBALS['G_SP']["auto_load_controller"])){
			return spClass($name)->__input($args);
		}elseif(!method_exists( $this, $name )){
			spError("方法 {$name}未定义！<br />请检查是否控制器类(".get_class($this).")与数据模型类重名？");
		}
	}

	/**
	 * 获取模板引擎实例
	 */
	public function getView()
	{
		$this->v->addfuncs();
		return $this->v->engine;
	}
	/**
	 * 设置当前用户的语言
     * @param $lang   语言标识
	 */
	public function setLang($lang)
	{
		if( array_key_exists($lang, $GLOBALS['G_SP']["lang"]) ){
			@ob_start();
			$domain = ('www.' == substr($_SERVER["HTTP_HOST"],0,4)) ? substr($_SERVER["HTTP_HOST"],4) : $_SERVER["HTTP_HOST"];
			setcookie("SpLangCookies", $lang, time()+31536000, '/', $domain ); // 一年过期
			$_SESSION["SpLangSession"] = $lang;
			return TRUE;
		}
		return FALSE;
	}
	/**
	 * 获取当前用户的语言
	 */
	public function getLang()
	{
		if( !isset($_COOKIE['SpLangCookies']) )return $_SESSION["SpLangSession"];
		return $_COOKIE['SpLangCookies'];
	}
}

/**
 * spArgs 
 * 应用程序变量类
 * spArgs是封装了$_SESSION、$_GET/$_POST、$_COOKIE、$_SERVER、$_FILES、$_ENV等，提供一些简便的访问和使用这些
 * 全局变量的方法。
 */

class spArgs {
	/**
	 * 在内存中保存的变量
	 */
	private $args = null;

	/**
	 * 构造函数
	 *
	 */
	public function __construct(){
        $this->args = $_REQUEST;
	}
	
	/**
	 * 获取应用程序请求变量值，同时也可以指定获取的变量所属。
	 * 
	 * @param name    获取的变量名称，如果为空，则返回全部的请求变量
	 * @param default    当前获取的变量不存在的时候，将返回的默认值
	 * @param method    获取位置，取值GET，POST，COOKIE
	 */
	public function get($name = null, $default = FALSE, $method = null)
	{
		if(null != $name){
			if( $this->has($name) ){
				if( null != $method ){
					switch (strtolower($method)) {
						case 'get':
							return $_GET[$name];
						case 'post':
							return $_POST[$name];
						case 'cookie':
							return $_COOKIE[$name];
					}
				}
				return $this->args[$name];
			}else{
				return (FALSE === $default) ? FALSE : $default;
			}
		}else{
			return $this->args;
		}
	}

	/**
	 * 设置（增加）环境变量值，该名称将覆盖原来的环境变量名称
	 * 
	 * @param name    环境变量名称
	 * @param value    环境变量值
	 */
	public function set($name, $value)
	{
		$this->args[$name] = $value;
	}

	/**
	 * 检测是否存在某值
	 * 
	 * @param name    待检测的环境变量名称
	 */
	public function has($name)
	{
		return isset($this->args[$name]);
	}

	/**
	 * 构造输入函数，标准用法
	 * @param args    环境变量名称的参数
	 */
	public function __input($args = -1)
	{
		if( -1 == $args )return $this;
		@list( $name, $default, $method ) = $args;
		return $this->get($name, $default, $method);
	}
	
	/**
	 * 获取请求字符
	 */
	public function request(){
		return $_SERVER["QUERY_STRING"];
	}
}

/**
 * zxcache 
 * 页面缓存类
 * author: 张鑫
 */
class zxcache extends spController{
	
	private $controller;
	private $action;
	
	public function __construct($controller, $action){
		$this->__set("controller", $controller);
		$this->__set("action", $action);
	}
	
	public function run(){
		if ($this->spArgs('cache') == 'cache'){
			//如果cache参数值为cahche, 则写入缓存
			if ($GLOBALS['spConfig']['ext']['zx_cache']['is_on'] && $GLOBALS['spConfig']['ext']['zx_cache']['cache_dir']) {
				$this->zxcache_write();
			}
		}elseif ($this->spArgs('cache') !== 'nocache') {
			//如果cache参数值不为nocache, 则读取缓存
			if ($GLOBALS['spConfig']['ext']['zx_cache']['is_on'] && $GLOBALS['spConfig']['ext']['zx_cache']['cache_dir']) {
				list($default_controller, $default_action) = $GLOBALS['spConfig']['home_url'];
				$name = $this->format($this->cachename($_SERVER['QUERY_STRING']));
				$cache_path = $GLOBALS['spConfig']['ext']['zx_cache']['cache_dir']. DS .$name . ".php";
				if (file_exists($cache_path)) {
					$fhandle = fopen($cache_path, 'r');
					$fcontent = fread($fhandle, filesize($cache_path));
					fclose($fhandle);
					$findex = strpos($fcontent, "<?");
					$fend = strpos($fcontent, "?>") + 2; 
					$pattern = '|expiretime=(.*);|isU'; 
					preg_match_all($pattern, $fcontent, $expiretimearray); 
					$expiretime = intval($expiretimearray[1][0]);
					if (time() > $expiretime) {
						$this->zxcache_write();
					}else{
						$fcontent = substr($fcontent, $fend);
					}
					fclose($handle);
					echo $fcontent;
					exit();
				}else{
					$this->zxcache_write();
				}
			}
		}
	}
	
	//返回缓存目标的url地址
	private function cacheurl($url){
		$suffix = $this->isspurlrewrite($GLOBALS['spConfig']) ? $GLOBALS['spConfig']['ext']['spUrlRewrite']['suffix'] : "";
		$web_root = str_replace("//", "/", WEB_ROOT ? WEB_ROOT."/" : WEB_ROOT);
		$url = str_replace("//", "/", str_replace($web_root, "", $url));
		$url = $this->format($url);
		if (!$url) {
			list($default_controller, $default_action) = $GLOBALS['spConfig']['home_url'];
			$cacheurl = str_replace($suffix, "", str_replace($web_root, "", str_replace("cachestr", "", spUrl($default_controller, $default_action."cachestr"))));
			$cacheurl = $this->format($cacheurl);
			return $cacheurl;
		}else{
			return str_replace($suffix, "", $url);
		}
	}
	
	//生成缓存文件所要访问的url
	private function cachewholeurl($url){
		$suffix = $this->isspurlrewrite($GLOBALS['spConfig']) ? $GLOBALS['spConfig']['ext']['spUrlRewrite']['suffix'] : "";
		$web_root = str_replace("//", "/", WEB_ROOT ? WEB_ROOT ."/" : WEB_ROOT);
		$url = str_replace("//", "/", str_replace($web_root, "", str_replace($suffix, "", $url)));
		$url = $this->format($url);
		if (!$url || $url == "/") {
			list($default_controller, $default_action) = $GLOBALS['spConfig']['home_url'];
			$url = str_replace($suffix, "", str_replace($web_root, "", str_replace("cachestr", "", spUrl($default_controller, $default_action."cachestr", array("cache"=>"nocache")))));
		}elseif ($this->isspurlrewrite($GLOBALS['spConfig'])){
			$url .= "/cache/nocache";
		}else{
			$url .= "&cache=nocache";
		}
		return str_replace("//", "/", $_SERVER['HTTP_HOST'] . $web_root . "/". $url . $suffix);
	}
	
	//返回缓存文件名
	private function cachename($url){
		$suffix = $this->isspurlrewrite($GLOBALS['spConfig']) ? $GLOBALS['spConfig']['ext']['spUrlRewrite']['suffix'] : "";
		$replacecache = $this->isspurlrewrite($GLOBALS['spConfig']) ? "/cache/nocache" : "&cache=nocache";
		$web_root = str_replace("//", "/", WEB_ROOT ? WEB_ROOT."/" : WEB_ROOT);
		$url = str_replace($replacecache, "", str_replace($web_root, "", str_replace($suffix, "", str_replace($_SERVER['HTTP_HOST'], "", $url))));
		$url = $this->format($url);
		if (!$url) {
			list($default_controller, $default_action) = $GLOBALS['spConfig']['home_url'];
			$url = str_replace($suffix, "", str_replace($web_root, "", str_replace("cachestr", "", spUrl($default_controller, $default_action."cachestr"))));
		}
		return md5($url);
	}
	
	//判断是否开启rewrite
	private function isspurlrewrite($spConfig){
		foreach ($spConfig['launch']['router_prefilter'] as $key => $val){
			if (in_array("spUrlRewrite", $val) && in_array("setReWrite", $val)) {
				return true;
			}
		}
		return false;
	}
	
	private function zxcache_write(){
		import("Curl_HTTP_Client.php");
		$curl = new Curl_HTTP_Client ( 0 );
		$cache_url = $this->cacheurl(spUrl($this->controller, $this->action, array_filter($this->spArgs())));
		$cache_whole_url = $this->cachewholeurl($cache_url);
		//$name = $this->format($this->cachename($cache_whole_url));
		$name = $this->format($this->cachename($_SERVER['QUERY_STRING']));
		$page_html = $curl->fetch_url($cache_whole_url);
		/*$cache_html = "<?php exit();url=".$this->format($_SERVER['QUERY_STRING']).";expiretime=".(time() + $GLOBALS['spConfig']['ext']['zx_cache']['cache_expiretime']).";?>";*/
		$cache_html = "<?php exit();expiretime=".(time() + $GLOBALS['spConfig']['ext']['zx_cache']['cache_expiretime']).";?>";
		$cache_html .= $page_html;
		$cache_path = $GLOBALS['spConfig']['ext']['zx_cache']['cache_dir']. DS .$name . ".php";
		if (!file_exists($GLOBALS['spConfig']['ext']['zx_cache']['cache_dir'])) {
			mkdir($GLOBALS['spConfig']['ext']['zx_cache']['cache_dir']);
		}
		$fp = fopen($cache_path, 'w');
		fputs($fp,$cache_html);
		fclose($fp);
		echo $page_html;
		exit();
	}
	
	//格式化url
	private function format($str){
		if (substr($str, -1) == "/") $str = substr($str, 0, strlen($str)-1);
		if (substr($str, 0, 1) == "/") $str = substr($str, 1);
		return strtolower($str);
	}
	
	public function __set($param, $value){
		$this->$param = $value;
	}
	
	public function __get($param){
		return $this->$param;
	}
}

